package com.bestcem.xm.survey.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSONArray;
import com.bestcem.xm.survey.enums.DataTypeEnum;
import com.bestcem.xm.survey.enums.TitleTypeEnum;
import com.bestcem.xm.survey.util.ColumnConstant;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public interface ConditionAnalysisService {
    /**
     * 题目id在map中的标识
     */
    String QTYPE = "qtype";

    /**
     * 危险词检查.处理匹配的危险词列表和
     *
     * @param answer   单题答案
     * @param value    字符串
     * @param checkKey key
     * @return Map<String, Object>
     * @author jy.zhao
     * @date 2021/8/18 17:01
     **/
    default Map<String, Object> checkWord(Object answer, ValueEntityDto value, String checkKey) {
        // 危险词列表
        List<String> dangerWordList = value.getWordList();
        if (CollUtil.isEmpty(dangerWordList)) {
            dangerWordList = new ArrayList<>();
        }
        // 需要匹配的规则
        String patternRule = dangerWordList.stream().filter(Validator::isNotEmpty).collect(Collectors.joining("|"));

        // 反词列表
        List<String> excludeList = value.getExcludeList();
        if (CollUtil.isEmpty(excludeList)) {
            excludeList = new ArrayList<>();
        }
        Map<String, Object> result = new HashMap<>();
        checkWordObject(answer, checkKey, patternRule, excludeList, result);
        return result;
    }

    /**
     * 替换答案字符串中危险词及相反的词
     *
     * @param answer          单题答案
     * @param patternRule     危险词匹配规则
     * @param excludeWordList 反词列表
     * @return List<Object>
     * @author jy.zhao
     * @date 2021/8/19 13:47
     **/
    default List<int[]> checkReplaceWordStr(String answer, String patternRule, List<String> excludeWordList) {
        // 替换反词列表
        for (String excludeWord : excludeWordList) {
            answer = answer.replaceAll(excludeWord, String.join("", Collections.nCopies(excludeWord.length(), "\b")));
        }
        Pattern pattern = Pattern.compile(patternRule);
        Matcher m = pattern.matcher(answer);
        List<int[]> result = new ArrayList<>();
        while (m.find()) {
            result.add(new int[]{m.start(), m.end() - m.start()});
        }
        return result;
    }

    /**
     * 解析检查对象中嵌套的危险词
     *
     * @param answer          答案
     * @param key             map中存储的结果key。 以#间隔
     * @param patternRule     匹配的危险词规则
     * @param excludeWordList 相反的反词列表
     * @param result          结果
     */
    default void checkWordObject(Object answer, String key, String patternRule, List<String> excludeWordList, Map<String, Object> result) {
        if (isInstance(answer, DataTypeEnum.LIST)) {
            List<Object> list = toList(answer);
            for (int i = 0; i < list.size(); i++) {
                checkWordObject(list.get(i), key.concat("#").concat(String.valueOf(i)), patternRule, excludeWordList, result);
            }
        } else if (isInstance(answer, DataTypeEnum.STRING)) {
            // 答案匹配结果。int[] 长度为2. 匹配的开始位置以及匹配的长度
            List<int[]> wordPatternResult = checkReplaceWordStr((String) answer, patternRule, excludeWordList);
            if (CollUtil.isNotEmpty(wordPatternResult)) {
                result.put(key, wordPatternResult);
            }
        } else if (isInstance(answer, DataTypeEnum.MAP)) {
            Map<String, Object> subResult;
            if (Validator.isNotEmpty(key)) {
                subResult = new HashMap<>();
            } else {
                subResult = result;
            }
            Map<String, Object> answerMap = toMap(answer);
            for (Map.Entry<String, Object> entry : answerMap.entrySet()) {
                if (Validator.isNotEmpty(entry.getKey())) {
                    checkWordObject(entry.getValue(), String.valueOf(entry.getKey()), patternRule, excludeWordList, subResult);
                }
            }
            if (Validator.isNotEmpty(key) && MapUtil.isNotEmpty(subResult)) {
                result.put(key, subResult);
            }
        }
    }

    /**
     * 获取分数
     *
     * @param answer 答案
     * @param opSeq  答卷编号
     * @param rowId  id
     * @return Integer python使用的地方会转成int，所以在这里直接返回int
     * @author jy.zhao
     * @date 2021/8/20 10:50
     **/
    Integer getScore(Object answer, String opSeq, String rowId);

    /**
     * 判断sourceQ中的元素是否选择题
     *
     * @param sourceQ 答案
     * @return boolean
     * @author jy.zhao
     * @date 2021/8/20 21:49
     **/
    default boolean isChoiceTitle(Object sourceQ) {
        return TitleTypeEnum.MATRIX_SINGLE.getName().equals(getAttr(sourceQ, QTYPE, String.class)) ||
                TitleTypeEnum.SORT.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否 矩阵单选题
     *
     * @param sourceQ 题目
     * @return boolean
     * @author jy.zhao
     * @date 2021/9/1 19:11
     **/
    default boolean isMatrixSingle(Object sourceQ) {
        return TitleTypeEnum.MATRIX_SINGLE.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否 矩阵多选题
     *
     * @param sourceQ 题目
     * @return boolean
     * @author jy.zhao
     * @date 2021/9/1 19:11
     **/
    default boolean isMatrixMultiple(Object sourceQ) {
        return TitleTypeEnum.MATRIX_MULTIPLE.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否多选
     *
     * @param sourceQ 题目
     * @return boolean
     * @author jy.zhao
     * @date 2021/9/1 19:23
     **/
    default boolean isMultiple(Object sourceQ) {
        return TitleTypeEnum.MULTIPLE.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否评价题
     *
     * @param sourceQ 答案
     * @return boolean
     * @author jy.zhao
     * @date 2021/8/20 21:41
     **/
    default boolean isEvaluateTitle(Object sourceQ) {
        return TitleTypeEnum.EVALUATION.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否城市地理题
     *
     * @param sourceQ 答案
     * @return Boolean
     * @author jy.zhao
     * @date 2021/8/20 21:44
     **/
    default boolean isCityTitle(Object sourceQ) {
        return TitleTypeEnum.CITY.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否单多选
     *
     * @param sourceQ 答案
     * @return Boolean
     * @author jy.zhao
     * @date 2021/8/20 21:44
     **/
    default boolean isSingleMultipleTitle(Object sourceQ) {
        return TitleTypeEnum.SINGLE.getName().equals(getAttr(sourceQ, QTYPE, String.class)) ||
                TitleTypeEnum.MULTIPLE.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 判断sourceQ中的元素是否单多选
     *
     * @param sourceQ 答案
     * @return Boolean
     * @author jy.zhao
     * @date 2021/8/20 21:44
     **/
    default boolean isSingleTitle(Object sourceQ) {
        return TitleTypeEnum.SINGLE.getName().equals(getAttr(sourceQ, QTYPE, String.class));
    }

    /**
     * 处理OptionIdDTO。
     *
     * @param optionIdDTO 选项id
     * @return Object
     * @author jy.zhao
     * @date 2021/9/1 16:02
     **/
    default Object dumpOption(OptionIdDto optionIdDTO) {
        if (Validator.isNotEmpty(optionIdDTO.getRowId())) {
            return optionIdDTO;
        }
        return optionIdDTO.getOptionId();
    }

    /**
     * 转换对象.在使用前使用 isInstance 校验类型
     *
     * @param obj 对象
     * @return List<Object> 结果
     * @author jy.zhao
     * @date 2021/8/27 23:27
     **/
    default List<Object> toList(Object obj) {
        return Convert.toList(Object.class, obj);
        //return (List<Object>) obj;
    }

    /**
     * 转换对象.在使用前使用 isInstance 校验类型
     *
     * @param obj 对象
     * @return Map<String, Object> 结果
     * @author jy.zhao
     * @date 2021/8/27 23:27
     **/
    default Map<String, Object> toMap(Object obj) {
        return Convert.toMap(String.class, Object.class, obj);
        //return (Map<String, Object>) obj;
    }

    /**
     * 根据key获取对象中的数值
     *
     * @param obj   对象
     * @param key   key
     * @param clazz 类型
     * @return T
     * @author jy.zhao
     * @date 2021/8/27 19:55
     **/
    default <T> T getAttr(Object obj, String key, Class<T> clazz) {
        // 如果不是map，直接返回null
        if (!(isInstance(obj, DataTypeEnum.MAP))) {
            return null;
        }
        // 获取到key对应的值
        T cast;
        //Object value = ((Map) obj).get(key);
        Object value = toMap(obj).get(key);
        try {
            cast = clazz.cast(value);
        } catch (ClassCastException e) {
            cast = null;
        }
        return cast;
    }

    /**
     * 判断类型
     *
     * @param object       需要判断的对象
     * @param dataTypeEnum 类型枚举
     * @return boolean
     * @author jy.zhao
     * @date 2021/8/27 19:02
     **/
    default boolean isInstance(Object object, DataTypeEnum dataTypeEnum) {
        if (dataTypeEnum.getIndex() == DataTypeEnum.INTEGER.getIndex()) {
            return object instanceof Integer;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.STRING.getIndex()) {
            return object instanceof String;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.DOUBLE.getIndex()) {
            return object instanceof Double;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.LIST.getIndex()) {
            return object instanceof List;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.MAP.getIndex()) {
            return object instanceof Map;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.BIG_DECIMAL.getIndex()) {
            return object instanceof BigDecimal;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.INTEGER_STRING.getIndex()) {
            return object instanceof Integer || object instanceof String;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.INTEGER_DOUBLE.getIndex()) {
            return object instanceof Integer || object instanceof Double;
        }
        if (dataTypeEnum.getIndex() == DataTypeEnum.INTEGER_DOUBLE_STRING.getIndex()) {
            return object instanceof Integer || object instanceof Double || object instanceof String;
        }
        return false;
    }

    /**
     * 对于单选题： 返回[选项id]
     * 对于多选题： 返回[选项1id, 选项2id...]
     * 对于打分题： 返回[分值]
     * 对于排序题： 返回[序号]
     * 对于级联题： 返回对应级的[选项id]
     * 对于城市题： 返回对应级的城市名称
     */
    default JSONArray getOptions(Object answer) {
        JSONArray options = new JSONArray();
        if (answer instanceof Integer || answer instanceof String) {
            options.add(answer);
        }
        if (answer instanceof List) {
            for (Object ops : (JSONArray) answer) {
                if (ops instanceof List) {
                    options.add(((List<?>) ops).get(0));
                } else if (ops instanceof Integer) {
                    options.add(ops);
                }
            }
        }
        return options;
    }

    boolean checkNestConditions(Long seq, String conditions, String projectId);

    @Data
    class ValueEntityDto {

        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.NAIRE_OPTIONS)
        private List<NaireOptions> naireOptions;

        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.OPTION_LIST)
        private List<Object> optionList;

        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.EVALUATION_TYPE)
        private Integer evaluationType;

        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.OPTION_MATCH)
        private String optionMatch;

        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.WORD_LIST)
        private List<String> wordList;

        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.EXCLUDE_LIST)
        private List<String> excludeList;

        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.IS_OPEN)
        private Boolean isOpen;


        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.CLASSIFY)
        private Options classify;

        /**
         * 过期时间。单位秒s
         */
        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.EXPIRE)
        private Long expire;

        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.SORT)
        private Integer sort;

        /**
         * 时间类型的题目携带字段
         */
        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.TIME)
        private List<String> time;

        /**
         * 分数类型最小分数
         */
        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.MIN_SCORE)
        private Integer minScore;

        /**
         * 分数类型最大分数
         */
        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.MAX_SCORE)
        private Integer maxScore;

        /**
         * 过期天数
         */
        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.DAYS)
        private Integer days;

        /**
         * 分数类型最小分数边界
         */
        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.LEFT_OPEN)
        private Boolean leftOpen;

        /**
         * 分数类型最大分数边界
         */
        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.RIGHT_OPEN)
        private Boolean rightOpen;

        /**
         * 日期类型的题目携带字段
         */
        @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.DATE)
        private List<String> date;

        /**
         * 前端参数
         */
        @JsonProperty(value = "again_d_t")
        private String againDt;
        /**
         * 前端参数
         */
        @JsonProperty(value = "type")
        private Integer type;
        /**
         * 前端参数
         */
        @JsonProperty(value = "raw_project_id")
        private String rawProjectId;
        /**
         * 前端参数
         */
        @JsonProperty(value = "deliver_project_id")
        private String deliverProjectId;
        /**
         * 前端参数
         */
        @JsonProperty(value = "qtype")
        private String qType;

        @Data
        public static class NaireOptions {

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.NaireOptions.OPTIONS)
            private List<Options> options;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.NaireOptions.TYPE)
            private String type;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.NaireOptions._ID)
            private String id;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.NaireOptions.GID)
            private String gid;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.NaireOptions.OID)
            private Integer oid;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.NaireOptions.TITLE)
            private String title;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.NaireOptions.OPEN_ATTRS)
            private OpenAttrs openAttrs;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.NaireOptions.IS_OPEN)
            private Boolean isOpen;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Value.NaireOptions.SEQ)
            private Integer seq;
        }

        @Data
        public static class Options {

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Options.GID)
            private String gid;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Options.TYPE)
            private String type;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Options.SEQ)
            private Integer seq;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Options.OID)
            private Integer oid;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Options.TITLE)
            private String title;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Options.DISABLED)
            private Boolean disabled;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Options.IS_OPEN)
            private Boolean open;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.Options.OPEN_ATTRS)
            private OpenAttrs openAttrs;
        }

        @Data
        public static class OpenAttrs {

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.SourceQ.Item.OpenAttrs.REQUIRED)
            private Boolean required;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.SourceQ.Item.OpenAttrs.TYPE)
            private Integer type;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.SourceQ.Item.OpenAttrs.RANGE)
            private List<String> range;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.SourceQ.Item.OpenAttrs.UNIQUE)
            private String unique;

            @JsonProperty(value = ColumnConstant.TicketRule.Condition.Filter.SourceQ.Item.OpenAttrs.UNIT)
            private String unit;
        }
    }

    /**
     * OptionIdDTO
     *
     * @author jy.zhao
     * @version 1.0
     * @date 2021/8/20 20:20
     **/
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    class OptionIdDto {

        /**
         *
         */
        @JsonProperty("row_id")
        private String rowId;

        /**
         * 由于python存储为蛇形，所以实体类定义为这样
         */
        @JsonProperty("option_id")
        private String optionId;
    }

    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    class TitleHandleResultDto {
        /**
         * gid
         */
        private Integer operation;

        /**
         *
         */
        private Object data;
    }
}
